package com.luo.dt.seata.tcc.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luo.dt.common.execption.MsgRuntimeException;
import com.luo.dt.common.model.result.RespResult;
import com.luo.dt.seata.tcc.mapper.StorageMapper;
import com.luo.dt.seata.tcc.model.dto.DeductStorageDto;
import com.luo.dt.seata.tcc.model.entity.Storage;
import com.luo.dt.seata.tcc.service.IStorageService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * 库存信息 服务实现类
 *
 * @author luohq
 * @date 2022-12-03
 */
@Service
@Slf4j
public class StorageServiceImpl extends ServiceImpl<StorageMapper, Storage> implements IStorageService {


    private final String CACHE_STORAGE_KEY_FORMAT = "storage:%s";
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 分支事务仅使用普通@Transactional注解即可
     */
    @Override
    public RespResult deduct(DeductStorageDto deductStorageDto) {
        log.info("开始TCC分支事务，XID={}", RootContext.getXID());
        log.info("扣减商品库存，参数: {}", deductStorageDto);
        /** 模拟回滚异常 */
        if ("product-2".equals(deductStorageDto.getCommodityCode())) {
            throw new MsgRuntimeException("异常:模拟业务异常:Storage branch exception");
        }

        /** 扣减缓存中的商品库存 */
        String cacheKey = String.format(CACHE_STORAGE_KEY_FORMAT, deductStorageDto.getCommodityCode());
        Long cacheStorageCount = this.redisTemplate.opsForValue().decrement(cacheKey, deductStorageDto.getCount());
        log.info("扣减后的商品库存: {}={}", cacheKey, cacheStorageCount);
        if (cacheStorageCount < 0) {
            throw new MsgRuntimeException("扣减商品缓存库存失败!");
        }
        return RespResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean commitDeduct(BusinessActionContext context) {
        //获取事务上下文传递的参数
        DeductStorageDto deductStorageDto = context.getActionContext("deductStorageDto", DeductStorageDto.class);
        log.info("TCC提交成功, XID={}, deductStorageDto={}", context.getXid(), deductStorageDto);
        /** 扣减DB中的商品库存 */
        Integer retCount = this.baseMapper.deductStorage(deductStorageDto.getCommodityCode(), deductStorageDto.getCount());
        log.info("修改商品库存，结果: {}", retCount);
        //修改商品库存失败，则直接回滚
        if (0 >= retCount) {
            throw new MsgRuntimeException("修改商品库存失败!");
        }
        return true;
    }

    @Override
    public Boolean cancelDeduct(BusinessActionContext context) {
        //获取事务上下文传递的参数
        DeductStorageDto deductStorageDto = context.getActionContext("deductStorageDto", DeductStorageDto.class);
        log.warn("TCC回滚业务, XID={}, deductStorageDto={}", context.getXid(), deductStorageDto);
        /** 还原缓存中的商品库存 */
        String cacheKey = String.format(CACHE_STORAGE_KEY_FORMAT, deductStorageDto.getCommodityCode());
        Long cacheStorageCount = this.redisTemplate.opsForValue().increment(cacheKey, deductStorageDto.getCount());
        log.info("还原后的商品库存: {}={}", cacheKey, cacheStorageCount);
        return true;
    }
}
